package com.allwees.bs.module.pms.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.allwees.bs.core.modelbase.branch.BranchUser;
import com.allwees.bs.core.modelbase.branch.EBranchType;
import com.allwees.bs.core.modelbase.constant.ResultCode;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import com.allwees.bs.core.modelbase.importdata.ImportDataBean;
import com.allwees.bs.core.modelbase.importdata.ImportResult;
import com.allwees.bs.module.pms.constant.ProductUploadTemplateEnum;
import com.allwees.bs.module.pms.dto.ProductDto;
import com.allwees.bs.module.pms.dto.SkuDto;
import com.allwees.bs.module.pms.entity.Category;
import com.allwees.bs.module.pms.entity.Product;
import com.allwees.bs.module.pms.entity.Sku;
import com.allwees.bs.module.pms.req.AdjustRetailPriceReq;
import com.allwees.bs.module.pms.req.ProductInfoReq;
import com.allwees.bs.module.pms.req.SkuReq;
import com.allwees.bs.module.pms.req.query.OrderProductQueryReq;
import com.allwees.bs.module.pms.req.query.ProductQueryReq;
import com.allwees.bs.module.pms.service.RetailerProductService;
import com.allwees.bs.module.pms.util.ProductCodeUtil;
import com.allwees.bs.module.pms.util.ProductExportUtil;
import com.allwees.bs.module.pms.vo.ExportProductVo;
import com.allwees.bs.module.pms.vo.ExportSkuVo;
import com.allwees.bs.module.pms.vo.ImportProductReq;
import com.allwees.bs.module.pms.vo.excel.ImportProductSkuBean;
import com.allwees.bs.module.pms.wf.ERetailerProductAction;
import com.allwees.bs.module.pms.wf.ERetailerProductState;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.DateUtil;
import com.allwees.core.common.util.UuidUtil;
import com.allwees.core.common.util.encrypt.MD5Util;
import com.allwees.core.common.util.excel.ExcelUtil;
import com.allwees.core.dao.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yangylsky
 * @since 2019-08-21
 */
@Slf4j
@Service
public class RetailerProductServiceImpl extends MerchantProductServiceImpl implements RetailerProductService {

//    @Autowired
//    private TransactionTemplate transactionTemplate;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean apply(Product product, String opUuid) {
        Assert.notNull(product, "product must not be a null");
        Assert.isTrue(product.canApply(), "当前product状态[" + product.getEStatus() + "]不能提审");
        switch (product.getEStatus()) {
            case CREATED:
            case PUTOFF_EDITING:
                product.start(ERetailerProductState.START, ERetailerProductAction.RETAILER_COMMIT, opUuid);
                break;
            case AUDIT_REJECTED:
                product.signal(ERetailerProductAction.RETAILER_COMMIT, opUuid);
                break;
            default:
                throw new BusinessException(ResultCode.PARAM_ERROR,"未知的操作类型");
        }
        return updateByUuid(product);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean adjustRetailPrice(AdjustRetailPriceReq req) {
        Product product = findByUuid(req.getBo().getUuid());
        Assert.notNull(product, "找不到商品:" + req.getBo().getUuid());
        product.assertStatus(EStatus.NORMAL, EStatus.PUTOFF_ONESELF);
        product.start(ERetailerProductState.START, ERetailerProductAction.RETAILER_ADJUST_PRICE, req.getRetailerUuid());
        updateByUuid(product);
        for (SkuReq skuReq : req.getSkus()) {
            Sku sku = new Sku();
            sku.setUuid(skuReq.getUuid());
            sku.setRetailPrice(skuReq.getRetailPrice());
            skuService.updateByUuid(sku);
        }
        return true;
    }


    @Override
    public <T extends ImportProductSkuBean> void importProductsSkus(List<T> productsSkus, ImportResult importResult, ImportProductReq param) {
        BranchUser user = param.getBranchUser();
        String uuid = user.checkRetailerUuid();

        //导入过程中发现有些由于大小写而产生的重复sku，这里统一转成小写，为后续校验去重
        Set<String> existSpus = productDao
                .findAllCustomNosByRetailerUuid(uuid)
                .stream()
                .map(String::toLowerCase)
                .collect(Collectors.toSet());
        Set<String> existSkus = skuDao
                .findAllCustomNosByRetailerUuid(uuid)
                .stream()
                .map(String::toLowerCase)
                .collect(Collectors.toSet());

        param.setExistSpuNos(existSpus);
        param.setExistSkuNos(existSkus);
        long start = System.currentTimeMillis();
        setProperties(productsSkus, param);
        log.info(">>>导入产品-设置属性耗时:{} ms",System.currentTimeMillis() - start);
        start = System.currentTimeMillis();
        saveProductAndSku(productsSkus, importResult, param);
        log.info(">>>导入产品-入库耗时:{} ms,行数:{}",System.currentTimeMillis() - start,productsSkus.size());

        if (param.isAutoApply()) {
            start = System.currentTimeMillis();
            int count = autoApplyProducts(param.getResults().stream().map(e -> (Product)e).map(Product::getUuid).collect(Collectors.toSet()), user);
            log.info(">>>提审耗时:{} ms,条数:{}",System.currentTimeMillis() - start,count);
        }
    }

    /**
     * 初始化字段值
     * @param beans
     * @param param
     * @param <T>
     */
    private <T extends ImportProductSkuBean> void setProperties(List<T> beans, ImportProductReq param) {
        ProductUploadTemplateEnum platform = param.getUploadExcelPlatform();
        switch (platform) {
            //处理wish模板
            case WISH:
                setWishProperties(beans,param);
                break;
            //处理Shopify模板
            case SHOPIFY:
                setShopifyProperties(beans,param);
                break;
            //处理Vova模板
            case VOVA:
                setVovaProperties(beans,param);
                break;
        }
    }

    private <T extends ImportProductSkuBean> void setWishProperties(List<T> beans,ImportProductReq param){
        String categoryNo = param.getCategoryNo();
        String storeUuid = param.getStoreUuid();

        String productCustomNo = "";
        String productName = "";
        String description = "";
        String mainPhoto = "";
        String keywords = "";

        ImportProductSkuBean mainBean = null;
        for (T bean : beans) {
            bean.setCategoryNo(categoryNo);
            bean.setStoreUuid(storeUuid);
            if (StringUtils.isNotBlank(bean.getProductCustomNo())) {
                mainBean = bean;
                productCustomNo = bean.getProductCustomNo();
                productName = bean.getProductName();
                description = bean.getDescription();
                mainPhoto = bean.getMainPhoto();
                keywords = bean.getKeywords();
                continue;
            }
            bean.setProductCustomNo(productCustomNo);
            bean.setProductName(productName);
            bean.setDescription(description);
            bean.setMainPhoto(mainPhoto);
            bean.setKeywords(keywords);
            setProductPrice(mainBean,bean);
        }
    }

    private <T extends ImportProductSkuBean> void setShopifyProperties(List<T> beans,ImportProductReq param){
        String categoryNo = param.getCategoryNo();
        String storeUuid = param.getStoreUuid();

        String productName = "";
        String description = "";
        String mainPhoto = "";
        String keywords = "";
        String option1Name = "";
        String option2Name = "";
        String option3Name = "";
        String extraPhoto = "";
        String productCustNo = "";
        ImportProductSkuBean mainBean = null;
        for (ImportProductSkuBean bean : beans) {
            bean.setCategoryNo(categoryNo);
            bean.setStoreUuid(storeUuid);

            if (StringUtils.isNotBlank(bean.getProductName())) {
                mainBean = bean;
                //shopify没有固定格式的自定义spu编码，在解析文件时由我方统一生成
                productCustNo = UuidUtil.generate();
                bean.setProductCustomNo(productCustNo);
                productName = bean.getProductName();
                description = bean.getDescription();
                mainPhoto = bean.getMainPhoto();
                keywords = bean.getKeywords();
                option1Name = bean.getOption1Name();
                option2Name = bean.getOption2Name();
                option3Name = bean.getOption3Name();
                continue;
            }

            extraPhoto = bean.getMainPhoto();
            if (StringUtils.isNotBlank(extraPhoto) && mainBean != null) {
                mainBean.addExtraPhoto(extraPhoto);
            }
            bean.setProductCustomNo(productCustNo);
            bean.setProductName(productName);
            bean.setDescription(description);
            bean.setKeywords(keywords);
            bean.setOption1Name(option1Name);
            bean.setOption2Name(option2Name);
            bean.setOption3Name(option3Name);
            bean.setMainPhoto(mainPhoto);
            setProductPrice(mainBean,bean);
        }
    }

    private <T extends ImportDataBean> void setVovaProperties(List<T> beans,ImportProductReq param) {

    }

    private void setProductPrice(ImportProductSkuBean product,ImportProductSkuBean sku) {
        if (product == null) {
            return;
        }

        setDefaultPrice(product);
        setDefaultPrice(sku);

        if(product.getRetailPrice().compareTo(BigDecimal.ZERO) == 0 || product.getRetailPrice().compareTo(sku.getRetailPrice()) > 0){
            product.setRetailPrice(sku.getRetailPrice());
            product.setOriginalPrice(sku.getOriginalPrice());
            product.setShippingPrice(sku.getShippingPrice());
        }
    }

    private void setDefaultPrice(ImportProductSkuBean bean){
        if(bean.getRetailPrice() == null){
            bean.setRetailPrice(BigDecimal.ZERO);
        }

        if (bean.getOriginalPrice() == null) {
            bean.setOriginalPrice(BigDecimal.ZERO);
        }

        if(bean.getShippingPrice() == null){
            bean.setShippingPrice(BigDecimal.ZERO);
        }
    }

    private <T extends ImportProductSkuBean> void saveProductAndSku(List<T> beans, ImportResult importResult, ImportProductReq param) {
        String categoryNo = param.getCategoryNo();
        Category category = categoryService.findUniqueByField("no",categoryNo);
        Assert.isTrue(category != null,"类别[" + categoryNo + "]不存在");
        EBranchType branchType = param.getBranchType();
        BranchUser user = param.getBranchUser();
        Set<String> existCustomSpuNos = param.getExistSpuNos();
        Set<String> existCustomSkuNos = param.getExistSkuNos();
        Product product;

        for (T bean : beans) {
            bean.completion();
            bean.initData();
            long start = System.currentTimeMillis();
            String skuCustomNo = bean.getSkuCustomNo();

            if(StringUtils.isBlank(skuCustomNo)){
                //importResult.incIgnoreNum();
                //continue;
                skuCustomNo = MD5Util.md516(bean.getProductName() + bean.getColor() + bean.getSize());
                bean.setSkuCustomNo(skuCustomNo);
            }
            //统一成小写进行比较
            boolean isSkuExist = existCustomSkuNos.contains(skuCustomNo.toLowerCase());
            //sku重复的情况，直接忽略该行记录的导入、继续下一次循环
            if(isSkuExist){
                importResult.incExistNum();
                continue;
            }

            boolean isProductExist = existCustomSpuNos.contains(bean.getProductCustomNo().toLowerCase());
            if (!isProductExist) {
                product = createProduct(bean.toProduct(), branchType, user, existCustomSpuNos,category);
                param.addResult(product.getCustomNo(),product);
            }
            product = getProduct(param, bean);
            Sku sku = createSku(product, bean.toSku(), existCustomSkuNos);
            importResult.incNewNum();

            long end = System.currentTimeMillis();
            log.info(">>>导入一条记录耗时:{} ms,retailer:{},cust_sku:{},category:{}",end-start,user.getRetailerUuid(),sku.getCustomNo(),categoryNo);
        }
    }

    private <T extends ImportProductSkuBean> Product getProduct(ImportProductReq param,T bean){
        Product product;
        Object result = param.getResult(bean.getProductCustomNo());
        if(result != null){
            product = (Product) result;
        } else {
            product = productDao.findUniqueByField("custom_no", bean.getProductCustomNo());
        }
        return product;
    }

    private int autoApplyProducts(Set<String> uuids, BranchUser user) {
        if (CollectionUtil.isEmpty(uuids)) {
            return 0;
        }
        List<Product> products = findByUuids(uuids);
        String opUuid = user.getUuid();
        for(Product product : products) {
            int skuCount = skuDao.getSkuCount(product.getUuid());
            if(skuCount < 1){
                log.info(">>>product has no related sku,spu_uuid:{}",product.getUuid());
                continue;
            }
            apply(product, opUuid);
        }
        return uuids.size();
    }

    /**
     * 创建product信息
     *
     * @param product
     * @param branchType
     * @param user
     * @param existSpuCustomNos
     * @return
     */
    private Product createProduct(Product product, EBranchType branchType, BranchUser user, Set<String> existSpuCustomNos,Category category) {
        setProductMerchantUuid(product, branchType, user);
        if (StringUtils.isBlank(product.getNo())) {
            product.setNo(genProductNo(category));
        }
        save(product);
        productDao.updateDefNo(product.getId(),ProductCodeUtil.idToCode(product.getId()));
        existSpuCustomNos.add(product.getCustomNo().toLowerCase());
        return product;
//        return transactionTemplate.execute(status -> {
//
//        });
    }

    /**
     * 创建sku信息
     *
     * @param product
     * @param sku
     * @param existSkuCustomNos
     * @return
     */
    private Sku createSku(Product product, Sku sku, Set<String> existSkuCustomNos) {
        sku.setProductUuid(product.getUuid());
        sku.setProductNo(product.getValidNo());
        skuService.save(sku);
        existSkuCustomNos.add(sku.getCustomNo().toLowerCase());
        return sku;
//        return transactionTemplate.execute(status -> {
//
//        });
    }

    @Override
    public Page<ProductDto> happenOrderPage(Page page, ProductQueryReq req) {
        List<Sku> skus = skuDao.findHappenOrderList(req);
        if (CollectionUtils.isEmpty(skus)) {
            return PageUtil.create(page);
        }

        ProductQueryReq productQueryReq = new ProductQueryReq();
        Page<Product> page1 = productDao.page(page, productQueryReq);

        Map<String, List<Sku>> skuMap = skus.stream().collect(Collectors.groupingBy(Sku::getProductUuid, Collectors.toList()));

        List<ProductDto> products = new ArrayList<>();

        page1.getRecords().forEach(o -> {
            ProductDto productDto = ProductDto.of(o);

            List<Sku> skuList = skuMap.get(o.getUuid());
            if (skuList == null) {
                skuList = new ArrayList<>();
            }
            productDto.setSkus(skuList.stream().map(SkuDto::new).collect(Collectors.toList()));

            products.add(productDto);
        });
        return PageUtil.create(page).setRecords(products);
    }

    @Override
    public ProductDto happenOrder(String productUuid) {
        Assert.hasLength(productUuid, "product uuid is not empty");

        Product product = productDao.findByUuid(productUuid);
        Assert.notNull(productUuid, "product not exist");

        ProductQueryReq productQueryReq = new ProductQueryReq();
        List<Sku> happenOrderSkus = skuDao.findHappenOrderList(productQueryReq);

        ProductDto dto = ProductDto.of(product);

        List<SkuDto> skuDtos = new ArrayList<>();
        happenOrderSkus.forEach(o -> skuDtos.add(new SkuDto(o)));

        dto.setSkus(skuDtos);

        return dto;
    }

    @Override
    public void exportHappenOrder(HttpServletResponse response, OrderProductQueryReq req) {
        Page<ProductDto> page = new Page<>(0, Integer.MAX_VALUE);
        page = happenOrderPage(page, req);
        if (page.getTotal() == 0) {
            return;
        }

        List<ExportProductVo> exportVos = ProductExportUtil.toExportVo(page.getRecords());

        String fileName = DateUtil.formatDate(new Date()) + "商品导出";
        ExcelUtil.writeExcel(response, exportVos, fileName, "商品", ExportProductVo.class);
    }

    @Override
    public void exportHappenedOrderSkus(HttpServletResponse response, OrderProductQueryReq req) {
        String fileName = DateUtil.formatDate(new Date()) + "商品Sku导出";

        List<Sku> skus = skuDao.findHappenOrderList(req);
        if (CollectionUtils.isEmpty(skus)) {
            ExcelUtil.writeExcel(response, new ArrayList<>(), fileName, "Sku", ExportSkuVo.class);
            return;
        }

        List<Product> products = productDao.findList(req);

        Map<String, Product> productMap = products.stream().collect(Collectors.toMap(Product::getUuid, o -> o));
        Set<String> categoryNos = products.stream().map(Product::getCategoryNo).collect(Collectors.toSet());

        Map<String, Category> categoryMap = categoryDao.selectByNos(categoryNos);

        List<ExportSkuVo> skuExportVos = ProductExportUtil.skuToExportVo(categoryMap, productMap, skus);

        ExcelUtil.writeExcel(response, skuExportVos, fileName, "SKU", ExportSkuVo.class);
    }

    @Override
    public void updateCustomNos(String productUuid, ProductInfoReq req) {
        Assert.hasLength(productUuid, "product uuid is not empty");

        Product product = productDao.findByUuid(productUuid);
        Assert.hasLength(productUuid, "product uuid is not exist");

        if (StringUtils.isBlank(product.getCustomNo()) || !product.getCustomNo().equalsIgnoreCase(req.getBo().getCustomNo())) {
            Product productUpdate = new Product();
            productUpdate.setCustomNo(req.getBo().getCustomNo());
            productUpdate.setNoUpdatedCount(product.getNoUpdatedCount() + 1);
            productDao.updateByUuid(productUpdate, productUuid);
        }

        Map<String, Sku> skuMap = skuDao.selectByProductUuid(productUuid);

        List<SkuReq> skus = req.getSkus();
        if (CollectionUtils.isEmpty(skus)) {
            return;
        }
        skus.forEach(skuReq -> {
            Sku sku = skuMap.get(skuReq.getUuid());
            if (sku == null) {
                return;
            }
            Sku skuUpdate = new Sku();
            skuUpdate.setCustomNo(skuReq.getCustomNo());
            skuUpdate.setNoUpdatedCount(sku.getNoUpdatedCount() + 1);
            skuDao.updateByUuid(skuUpdate, skuReq.getUuid());
        });
    }
}
